package top.go2do.auth.business.role.service;

import jakarta.persistence.criteria.Predicate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import top.go2do.common.dao.ApplicationRepository;
import top.go2do.common.module.ApplicationPO;
import top.go2do.auth.business.permission.dao.PermissionRepository;
import top.go2do.auth.business.permission.module.PermissionPO;
import top.go2do.auth.business.permission.module.PermissionSimplyVO;
import top.go2do.auth.business.role.module.RolePermissionPO;
import top.go2do.auth.business.role.dao.RolePermissionRepository;
import top.go2do.auth.business.role.dao.RoleRepository;
import top.go2do.auth.business.role.module.*;
import top.go2do.common.dao.UserRepository;
import top.go2do.common.module.UserPO;
import top.go2do.auth.exception.AuthErrorCode;
import top.go2do.auth.exception.AuthException;
import top.go2do.common.module.pageQuery.PageResult;
import top.go2do.common.utils.IdUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author xingyj
 * @CreateTime 2025/6/12 11:37
 */
@Service
public class RoleServiceImpl implements RoleService {
    @Autowired
    private RoleRepository roleRepository;

    @Autowired
    private IdUtils idUtils;

    @Autowired
    private ApplicationRepository applicationRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RolePermissionRepository rolePermissionRepository;

    @Autowired
    private PermissionRepository permissionRepository;

    @Override
    public RolePO create(RoleCreateForm createForm) {
        String appId = createForm.getAppId();
        String roleCode = createForm.getRoleCode();
        String roleName = createForm.getRoleName();
        RolePO rolePO = roleRepository.findByAppIdAndRoleCode(appId, roleCode);
        if (rolePO != null) {
            throw new AuthException(AuthErrorCode.ROLE_CODE_EXIST);
        }
        rolePO = roleRepository.findByAppIdAndRoleName(appId, roleName);
        if (rolePO != null) {
            throw new AuthException(AuthErrorCode.ROLE_NAME_EXIST);
        }
        rolePO = new RolePO();
        BeanUtils.copyProperties(createForm, rolePO);
        Long roleId = idUtils.getSnowflakeId();
        Long userId = createForm.getUserId();

        rolePO.setRoleId(roleId);
        rolePO.setCreatedBy(userId);
        rolePO.setUpdatedBy(userId);
        roleRepository.save(rolePO);
        return this.getById(roleId);
    }

    @Override
    public void modifyById(RoleModifyForm modifyForm) {
        Long roleId = modifyForm.getRoleId();
        RolePO rolePO = this.getById(roleId);
        String roleName = modifyForm.getRoleName();
        String roleCode = modifyForm.getRoleCode();
        String appId = rolePO.getAppId();
        if (StringUtils.hasText(roleName)) {
            RolePO target = roleRepository.findByAppIdAndRoleName(appId, roleName);
            if (target != null && !target.getRoleId().equals(roleId)) {
                throw new AuthException(AuthErrorCode.ROLE_NAME_EXIST);
            }
            rolePO.setRoleName(roleName);
        }
        if (StringUtils.hasText(roleCode)) {
            RolePO target = roleRepository.findByAppIdAndRoleCode(appId, roleCode);
            if (target != null && !target.getRoleId().equals(roleId)) {
                throw new AuthException(AuthErrorCode.ROLE_CODE_EXIST);
            }
            rolePO.setRoleCode(roleCode);
        }
        rolePO.setUpdatedBy(modifyForm.getUserId());
        roleRepository.save(rolePO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeById(Long id) {
        this.getById(id);
        roleRepository.deleteById(id);
        rolePermissionRepository.deleteByRoleId(id);
    }

    @Override
    public RolePO getById(Long id) {
        RolePO rolePO = roleRepository.findById(id).orElse(null);
        if (rolePO == null) {
            throw new AuthException(AuthErrorCode.ROLE_ID_NOT_EXIST);
        }
        return rolePO;
    }

    @Override
    public PageResult<RolePO> pageQuery(RoleQueryCondition condition) {
        Sort.Direction direction = Sort.Direction.fromString(condition.getDirection());
        Sort sort = Sort.by(direction, condition.getOrderBy());

        /**
         * pageNumber 从0开始
         */
        Integer page = condition.getPage() - 1;
        Integer size = condition.getSize();
        Pageable pageable = PageRequest.of(page, size, sort);
        Specification<RolePO> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (condition.getAppId() != null) {
                predicates.add(cb.equal(root.get("appId"), condition.getAppId() ));
            }

            if (condition.getRoleName() != null) {
                predicates.add(cb.like(root.get("roleName"), "%" + condition.getRoleName() + "%"));
            }

            if (condition.getRoleCode() != null) {
                predicates.add(cb.like(root.get("roleCode"), "%" + condition.getRoleCode() + "%"));
            }

            if (condition.getKeyword() != null) {
                Predicate appIdMatch = cb.like(root.get("roleName"), "%" + condition.getKeyword() + "%");
                Predicate appNameMatch = cb.like(root.get("roleCode"), "%" + condition.getKeyword() + "%");
                predicates.add(cb.or(appIdMatch, appNameMatch)); // 核心修改点
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        Page<RolePO> queryResult = roleRepository.findAll(spec, pageable);
        PageResult<RolePO>  pageResult = new PageResult(queryResult.getNumber(), queryResult.getSize(), queryResult.getTotalElements(), queryResult.getTotalPages());
        pageResult.setList(queryResult.getContent());
        return pageResult;
    }

    @Override
    public List<RolePO> listAll(RoleQueryAllCondition condition) {
        Sort.Direction direction = Sort.Direction.fromString(condition.getDirection());
        Sort sort = Sort.by(direction, condition.getOrderBy());

        Specification<RolePO> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (condition.getAppId() != null) {
                predicates.add(cb.equal(root.get("appId"), condition.getAppId() ));
            }

            if (condition.getRoleName() != null) {
                predicates.add(cb.like(root.get("roleName"), "%" + condition.getRoleName() + "%"));
            }

            if (condition.getRoleCode() != null) {
                predicates.add(cb.like(root.get("roleCode"), "%" + condition.getRoleCode() + "%"));
            }

            if (condition.getKeyword() != null) {
                Predicate appIdMatch = cb.like(root.get("roleName"), "%" + condition.getKeyword() + "%");
                Predicate appNameMatch = cb.like(root.get("roleCode"), "%" + condition.getKeyword() + "%");
                predicates.add(cb.or(appIdMatch, appNameMatch)); // 核心修改点
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        return roleRepository.findAll(spec, sort);
    }

    @Override
    public List<RoleListVO> po2vo(List<RolePO> list) {
        Set<String> appIdSet = new HashSet<>();
        Set<Long> userIdSet = new HashSet<>();
        Set<Long> roleIdSet = new HashSet<>();

        for(RolePO po: list) {
            appIdSet.add(po.getAppId());
            userIdSet.add(po.getCreatedBy());
            userIdSet.add(po.getUpdatedBy());
            roleIdSet.add(po.getRoleId());
        }
        List<ApplicationPO> appList = applicationRepository.findAllById(appIdSet);
        List<UserPO> userList = userRepository.findAllById(userIdSet);
        List<RolePermissionPO> rolePermissionList = rolePermissionRepository.findByRoleIdIn(roleIdSet);
        Set<Long> permIdSet;
        if (rolePermissionList != null) {
            permIdSet = rolePermissionList.stream().map(RolePermissionPO::getPermId).collect(Collectors.toSet());
        } else {
            permIdSet = new HashSet<>();
        }
        List<PermissionPO> permList =  permissionRepository.findAllById(permIdSet);

        Map<String,String> appId2NameMap;
        Map<Long,String> userId2NameMap;
        Map<Long ,PermissionPO> permId2POMap;
        Map<Long, Set<PermissionSimplyVO>> roleId2PermListMap;
        if (appList != null) {
            appId2NameMap = appList.stream().collect(Collectors.toMap(ApplicationPO::getAppId,ApplicationPO::getAppName));
        } else {
            appId2NameMap = new HashMap<>(0);
        }

        if (userList != null) {
            userId2NameMap = userList.stream().collect(Collectors.toMap(UserPO::getUserId,UserPO::getUsername));
        } else {
            userId2NameMap = new HashMap<>(0);
        }
        if (permList != null) {
            permId2POMap = permList.stream().collect(Collectors.toMap(PermissionPO::getPermId, po -> po));
        } else {
            permId2POMap = new HashMap<>(0);
        }

        if (rolePermissionList != null) {
            roleId2PermListMap = new HashMap<>();
            for (RolePermissionPO rolePermPO: rolePermissionList) {
                Long roleId = rolePermPO.getRoleId();
                Long permId = rolePermPO.getPermId();
                Set<PermissionSimplyVO> permSet = roleId2PermListMap.get(roleId);
                if (permSet == null) {
                    permSet = new HashSet<>();
                }
                PermissionPO po = permId2POMap.get(permId);
                PermissionSimplyVO permVo = new PermissionSimplyVO();
                permVo.setPermId(String.valueOf(po.getPermId()));
                permVo.setPermName(po.getPermName());
                permVo.setPermCode(po.getPermCode());
                permSet.add(permVo);
                roleId2PermListMap.put(roleId, permSet);
            }
        } else {
            roleId2PermListMap = new HashMap<>(0);
        }

        List<RoleListVO> voList = list.stream().map(po -> {
            RoleListVO vo = new RoleListVO();
            BeanUtils.copyProperties(po, vo);
            vo.setAppName(appId2NameMap.get(po.getAppId()));
            vo.setPermList(roleId2PermListMap.get(po.getRoleId()));
            vo.setCreatedByName(userId2NameMap.get(po.getCreatedBy()));
            vo.setUpdatedByName(userId2NameMap.get(po.getUpdatedBy()));
            vo.setRoleId(String.valueOf(po.getRoleId()));
            vo.setUpdatedBy(String.valueOf(po.getUpdatedBy()));
            vo.setCreatedBy(String.valueOf(po.getCreatedBy()));
            return vo;
        }).toList();
        return voList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setPermissions(RolePermissionsForm form) {
        Long roleId = form.getRoleId();
        RolePO po = this.getById(roleId);
        po.setUpdatedBy(form.getUserId());
        roleRepository.save(po);
        String appId = po.getAppId();
        /**
         * step: 先删除所有，再插入最新
         */
        rolePermissionRepository.deleteByRoleId(roleId);
        List<RolePermissionPO> rpList = new ArrayList<>();
        form.getPermIdList().forEach(permId -> {
            RolePermissionPO rolePermissionPO = new RolePermissionPO();
            rolePermissionPO.setRoleId(roleId);
            rolePermissionPO.setPermId(permId);
            rolePermissionPO.setAppId(appId);
            rpList.add(rolePermissionPO);
        });
        rolePermissionRepository.saveAll(rpList);
    }
}
